package io.task.cc.network;

import io.task.cc.network.spi.client.ClientChannelContext;
import io.task.cc.network.spi.client.ClientConfig;
import io.task.cc.network.spi.client.ReconnConf;
import io.task.cc.network.spi.server.ServerConfig;
import io.task.cc.network.utils.lock.LockedSet;
import io.task.cc.network.utils.lock.ReadLockHandler;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;

@Slf4j
@Setter
@Getter
public class Io {

    /**
     * 绑定业务id
     */
    public static void bindBsId(ChannelContext channelContext, String bsId) {
        channelContext.config.bsIds.bind(bsId, channelContext);
    }

    /**
     * 绑定群组
     */
    public static void bindGroup(ChannelContext channelContext, String group) {
        channelContext.config.groups.bind(group, channelContext);
    }

    /**
     * 将用户绑定到群组
     */
    public static void bindGroup(Config config, String userid, String group) {
        LockedSet<ChannelContext> lockedSet = Io.getByUserid(config, userid);
        if (lockedSet != null) {
            lockedSet.handle(new ReadLockHandler<Set<ChannelContext>>() {
                @Override
                public void handle(Set<ChannelContext> set) {
                    for (ChannelContext channelContext : set) {
                        Io.bindGroup(channelContext, group);
                    }
                }
            });
        }
    }

    /**
     * 绑定token
     */
    public static void bindToken(ChannelContext channelContext, String token) {
        channelContext.config.tokens.bind(token, channelContext);
    }

    /**
     * 绑定用户
     */
    public static void bindUser(ChannelContext channelContext, String userid) {
        channelContext.config.users.bind(userid, channelContext);
    }

    /**
     * 阻塞发送消息到指定ChannelContext
     */
    public static Boolean bSend(ChannelContext channelContext, Packet packet) {
        if (channelContext == null) {
            return false;
        }
        CountDownLatch countDownLatch = new CountDownLatch(1);
        return send(channelContext, packet, countDownLatch, PacketSendMode.SINGLE_BLOCK);
    }

    /**
     * 发送到指定的ip和port
     */
    public static Boolean bSend(Config config, String ip, int port, Packet packet) {
        return send(config, ip, port, packet, true);
    }

    /**
     * 发消息到所有连接
     */
    public static Boolean bSendToAll(Config config, Packet packet, ChannelContextFilter channelContextFilter) {
        return sendToAll(config, packet, channelContextFilter, true);
    }

    /**
     * 阻塞发消息给指定业务ID
     */
    public static Boolean bSendToBsId(Config config, String bsId, Packet packet) {
        return sendToBsId(config, bsId, packet, true);
    }

    /**
     * 发消息到组
     */
    public static Boolean bSendToGroup(Config config, String group, Packet packet) {
        return bSendToGroup(config, group, packet, null);
    }

    /**
     * 发消息到组
     */
    public static Boolean bSendToGroup(Config config, String group, Packet packet, ChannelContextFilter channelContextFilter) {
        return sendToGroup(config, group, packet, channelContextFilter, true);
    }

    /**
     * 发消息给指定ChannelContext id
     */
    public static Boolean bSendToId(Config config, String channelContextId, Packet packet) {
        return sendToId(config, channelContextId, packet, true);
    }

    /**
     * 阻塞发送到指定ip对应的集合
     */
    public static Boolean bSendToIp(Config config, String ip, Packet packet) {
        return bSendToIp(config, ip, packet, null);
    }

    /**
     * 阻塞发送到指定ip对应的集合
     */
    public static Boolean bSendToIp(Config config, String ip, Packet packet, ChannelContextFilter channelContextFilter) {
        return sendToIp(config, ip, packet, channelContextFilter, true);
    }

    /**
     * 发消息到指定集合
     */
    public static Boolean bSendToSet(Config config, LockedSet<ChannelContext> lockedSet, Packet packet, ChannelContextFilter channelContextFilter) {
        return sendToSet(config, lockedSet, packet, channelContextFilter, true);
    }

    /**
     * 阻塞发消息到指定token
     */
    public static Boolean bSendToToken(Config config, String token, Packet packet) {
        return sendToToken(config, token, packet, true);
    }

    /**
     * 阻塞发消息给指定用户
     */
    public static Boolean bSendToUser(Config config, String userid, Packet packet) {
        return sendToUser(config, userid, packet, true);
    }

    /**
     * 关闭连接
     */
    public static void close(ChannelContext channelContext, String remark) {
        close(channelContext, null, remark);
    }


    public static void close(ChannelContext channelContext, String remark, CloseCode closeCode) {
        close(channelContext, null, remark, closeCode);
    }

    /**
     * 关闭连接
     */
    public static void close(ChannelContext channelContext, Throwable throwable, String remark) {
        close(channelContext, throwable, remark, false);
    }

    public static void close(ChannelContext channelContext, Throwable throwable, String remark, CloseCode closeCode) {
        close(channelContext, throwable, remark, false, closeCode);
    }

    public static void close(ChannelContext channelContext, Throwable throwable, String remark, boolean isNeedRemove) {
        close(channelContext, throwable, remark, isNeedRemove, true);
    }

    public static void close(ChannelContext channelContext, Throwable throwable, String remark, boolean isNeedRemove, CloseCode closeCode) {
        close(channelContext, throwable, remark, isNeedRemove, true, closeCode);
    }

    public static void close(ChannelContext channelContext, Throwable throwable, String remark, boolean isNeedRemove, boolean needCloseLock) {
        close(channelContext, throwable, remark, isNeedRemove, needCloseLock, null);
    }


    public static void close(ChannelContext channelContext, Throwable throwable, String remark, boolean isNeedRemove, boolean needCloseLock, CloseCode closeCode) {
        if (channelContext == null) {
            return;
        }
        if (channelContext.isWaitingClose) {
            log.debug("{} 正在等待被关闭", channelContext);
            return;
        }

        //先立即取消各项任务，这样可防止有新的任务被提交进来
        channelContext.decodeRunnable.setCanceled(true);
        channelContext.handleQueueRunnable.setCanceled(true);
        channelContext.sendQueueRunnable.setCanceled(true);

        WriteLock writeLock = null;
        if (needCloseLock) {
            writeLock = channelContext.closeLock.writeLock();

            boolean tryLock = writeLock.tryLock();
            if (!tryLock) {
                return;
            }
            channelContext.isWaitingClose = true;
            writeLock.unlock();
        } else {
            channelContext.isWaitingClose = true;
        }

        if (closeCode == null) {
            if (channelContext.getCloseCode() == CloseCode.INIT_STATUS) {
                channelContext.setCloseCode(CloseCode.NO_CODE);
            }
        } else {
            channelContext.setCloseCode(closeCode);
        }

        if (channelContext.asynchronousSocketChannel != null) {
            try {
                channelContext.asynchronousSocketChannel.shutdownInput();
            } catch (Throwable e) {
                //log.error(e.toString(), e);
            }
            try {
                channelContext.asynchronousSocketChannel.shutdownOutput();
            } catch (Throwable e) {
                //log.error(e.toString(), e);
            }
            try {
                channelContext.asynchronousSocketChannel.close();
            } catch (Throwable e) {
                //log.error(e.toString(), e);
            }
        }

        channelContext.closeMeta.setRemark(remark);
        channelContext.closeMeta.setThrowable(throwable);
        if (!isNeedRemove) {
            if (channelContext.isServer()) {
                isNeedRemove = true;
            } else {
                ClientChannelContext clientChannelContext = (ClientChannelContext) channelContext;
                if (!ReconnConf.isNeedReconn(clientChannelContext, false)) { //不需要重连
                    isNeedRemove = true;
                }
            }
        }
        channelContext.closeMeta.setNeedRemove(isNeedRemove);

        channelContext.config.closeRunnable.addMsg(channelContext);
        channelContext.config.closeRunnable.execute();
    }

    /**
     * 关闭连接
     */
    public static void close(Config config, String clientIp, Integer clientPort, Throwable throwable, String remark) {
        ChannelContext channelContext = config.clientNodes.find(clientIp, clientPort);
        close(channelContext, throwable, remark);
    }

    /**
     * 关闭某群所有连接
     */
    public static void closeIp(Config config, String ip, String remark) {
        closeIp(config, ip, remark, null);
    }

    /**
     * 关闭某群所有连接
     */
    public static void closeIp(Config config, String ip, String remark, CloseCode closeCode) {
        LockedSet<ChannelContext> lockedSet = Io.getByIp(config, ip);
        closeSet(config, lockedSet, remark, closeCode);
    }

    /**
     * 关闭某群所有连接
     */
    public static void closeGroup(Config config, String group, String remark) {
        closeGroup(config, group, remark, null);
    }

    /**
     * 关闭某群所有连接
     */
    public static void closeGroup(Config config, String group, String remark, CloseCode closeCode) {
        LockedSet<ChannelContext> lockedSet = Io.getByGroup(config, group);
        closeSet(config, lockedSet, remark, closeCode);
    }

    /**
     * 关闭用户的所有连接
     */
    public static void closeUser(Config config, String userid, String remark) {
        closeUser(config, userid, remark, null);
    }

    /**
     * 关闭某用户的所有连接
     */
    public static void closeUser(Config config, String userid, String remark, CloseCode closeCode) {
        LockedSet<ChannelContext> lockedSet = Io.getByUserid(config, userid);
        closeSet(config, lockedSet, remark, closeCode);
    }

    /**
     * 关闭token的所有连接
     */
    public static void closeToken(Config config, String token, String remark) {
        closeToken(config, token, remark, null);
    }

    /**
     * 关闭某token的所有连接
     */
    public static void closeToken(Config config, String token, String remark, CloseCode closeCode) {
        LockedSet<ChannelContext> lockedSet = Io.getByToken(config, token);
        closeSet(config, lockedSet, remark, closeCode);
    }

    /**
     * 关闭某群所有连接
     */
    public static void removeIp(Config config, String ip, String remark) {
        removeIp(config, ip, remark, null);
    }

    /**
     * 关闭某群所有连接
     */
    public static void removeIp(Config config, String ip, String remark, CloseCode removeCode) {
        LockedSet<ChannelContext> lockedSet = Io.getByIp(config, ip);
        removeSet(config, lockedSet, remark, removeCode);
    }

    /**
     * 关闭某群所有连接
     */
    public static void removeGroup(Config config, String group, String remark) {
        removeGroup(config, group, remark, null);
    }

    /**
     * 关闭某群所有连接
     */
    public static void removeGroup(Config config, String group, String remark, CloseCode removeCode) {
        LockedSet<ChannelContext> lockedSet = Io.getByGroup(config, group);
        removeSet(config, lockedSet, remark, removeCode);
    }

    /**
     * 关闭用户的所有连接
     */
    public static void removeUser(Config config, String userid, String remark) {
        removeUser(config, userid, remark, null);
    }

    /**
     * 关闭某用户的所有连接
     */
    public static void removeUser(Config config, String userid, String remark, CloseCode removeCode) {
        LockedSet<ChannelContext> lockedSet = Io.getByUserid(config, userid);
        removeSet(config, lockedSet, remark, removeCode);
    }

    /**
     * 关闭token的所有连接
     */
    public static void removeToken(Config config, String token, String remark) {
        removeToken(config, token, remark, null);
    }

    /**
     * 关闭某token的所有连接
     */
    public static void removeToken(Config config, String token, String remark, CloseCode removeCode) {
        LockedSet<ChannelContext> lockedSet = Io.getByToken(config, token);
        removeSet(config, lockedSet, remark, removeCode);
    }

    /**
     * 关闭集合
     */
    public static void closeSet(Config config, LockedSet<ChannelContext> lockedSet, String remark, CloseCode closeCode) {
        if (lockedSet != null) {
            lockedSet.handle(new ReadLockHandler<Set<ChannelContext>>() {
                @Override
                public void handle(Set<ChannelContext> set) {
                    for (ChannelContext channelContext : set) {
                        Io.close(channelContext, remark, closeCode);
                    }
                }
            });
        }
    }

    /**
     * 移除集合
     */
    public static void removeSet(Config config, LockedSet<ChannelContext> lockedSet, String remark, CloseCode closeCode) {
        if (lockedSet != null) {
            lockedSet.handle(new ReadLockHandler<Set<ChannelContext>>() {
                @Override
                public void handle(Set<ChannelContext> set) {
                    for (ChannelContext channelContext : set) {
                        Io.remove(channelContext, remark, closeCode);
                    }
                }
            });
        }
    }

    /**
     * 获取所有连接，包括当前处于断开状态的
     */
    public static LockedSet<ChannelContext> getAll(Config config) {
        return config.connections;
    }

    /**
     * 获取所有连接，包括当前处于断开状态的
     */
    public static LockedSet<ChannelContext> getAllChannelContexts(Config config) {
        return getAll(config);
    }

    /**
     * 此API仅供 tio client使用
     * 获取所有处于正常连接状态的连接
     */
    public static LockedSet<ChannelContext> getConnecteds(ClientConfig clientConfig) {
        return clientConfig.connecteds;
    }

    /**
     * 此API仅供 tio client使用
     * 获取所有处于正常连接状态的连接
     */
    public static LockedSet<ChannelContext> getAllConnectedsChannelContexts(ClientConfig clientConfig) {
        return getConnecteds(clientConfig);
    }

    /**
     * 根据业务id找ChannelContext
     */
    public static ChannelContext getByBsId(Config config, String bsId) {
        return config.bsIds.find(bsId);
    }

    /**
     * 根据业务id找ChannelContext
     */
    public static ChannelContext getChannelContextByBsId(Config config, String bsId) {
        return getByBsId(config, bsId);
    }

    /**
     * 根据clientip和clientport获取ChannelContext
     */
    public static ChannelContext getByClientNode(Config config, String clientIp, Integer clientPort) {
        return config.clientNodes.find(clientIp, clientPort);
    }

    /**
     * 根据clientip和clientport获取ChannelContext
     */
    public static ChannelContext getChannelContextByClientNode(Config config, String clientIp, Integer clientPort) {
        return getByClientNode(config, clientIp, clientPort);
    }

    /**
     * 根据ChannelContext.id获取ChannelContext
     */
    public static ChannelContext getByChannelContextId(Config config, String channelContextId) {
        return config.ids.find(channelContextId);
    }

    /**
     * 根据ChannelContext.id获取ChannelContext
     */
    public static ChannelContext getChannelContextById(Config config, String channelContextId) {
        return getByChannelContextId(config, channelContextId);
    }

    /**
     * 获取一个组的所有客户端
     */
    public static LockedSet<ChannelContext> getByGroup(Config config, String group) {
        return config.groups.clients(config, group);
    }

    /**
     * 获取一个组的所有客户端
     */
    public static LockedSet<ChannelContext> getChannelContextsByGroup(Config config, String group) {
        return getByGroup(config, group);
    }

    /**
     * 根据token获取SetWithLock<ChannelContext>
     */
    public static LockedSet<ChannelContext> getByToken(Config config, String token) {
        return config.tokens.find(token);
    }

    /**
     * 根据客户端ip获取SetWithLock<ChannelContext>
     */
    public static LockedSet<ChannelContext> getByIp(Config config, String ip) {
        return config.ips.clients(ip);
    }

    /**
     * 根据token获取SetWithLock<ChannelContext>
     */
    public static LockedSet<ChannelContext> getChannelContextsByToken(Config config, String token) {
        return getByToken(config, token);
    }

    /**
     * 根据userid获取SetWithLock<ChannelContext>
     */
    public static LockedSet<ChannelContext> getByUserid(Config config, String userid) {
        return config.users.find(userid);
    }

    /**
     * 根据userid获取SetWithLock<ChannelContext>
     */
    public static LockedSet<ChannelContext> getChannelContextsByUserid(Config config, String userid) {
        return getByUserid(config, userid);
    }


    /**
     * 某通道是否在某群组中
     */
    public static boolean isInGroup(String group, ChannelContext channelContext) {
        LockedSet<String> lockedSet = channelContext.getGroups();
        if (lockedSet == null) {
            return false;
        }

        Set<String> set = lockedSet.getT();
        if (set == null) {
            return false;
        }

        return set.contains(group);
    }

    public static void remove(ChannelContext channelContext, String remark) {
        remove(channelContext, remark, null);
    }

    /**
     * 和close方法对应，只不过不再进行重连等维护性的操作
     */
    public static void remove(ChannelContext channelContext, String remark, CloseCode closeCode) {
        remove(channelContext, null, remark, closeCode);
    }

    /**
     * 和close方法对应，只不过不再进行重连等维护性的操作
     */
    public static void remove(ChannelContext channelContext, Throwable throwable, String remark) {
        remove(channelContext, throwable, remark, null);
    }

    /**
     * 和close方法对应，只不过不再进行重连等维护性的操作
     */
    public static void remove(ChannelContext channelContext, Throwable throwable, String remark, CloseCode closeCode) {
        close(channelContext, throwable, remark, true, closeCode);
    }

    /**
     * 和close方法对应，只不过不再进行重连等维护性的操作
     */
    public static void remove(Config config, String clientIp, Integer clientPort, Throwable throwable, String remark) {
        remove(config, clientIp, clientPort, throwable, remark, null);
    }

    /**
     * 删除clientip和clientPort为指定值的连接
     */
    public static void remove(Config config, String clientIp, Integer clientPort, Throwable throwable, String remark, CloseCode closeCode) {
        ChannelContext channelContext = config.clientNodes.find(clientIp, clientPort);
        remove(channelContext, throwable, remark, closeCode);
    }

    /**
     * 删除clientip为指定值的所有连接
     */
    public static void remove(ServerConfig serverConfig, String ip, String remark) {
        remove(serverConfig, ip, remark, null);
    }

    /**
     * 删除clientip为指定值的所有连接
     */
    public static void remove(ServerConfig serverConfig, String ip, String remark, CloseCode closeCode) {
        LockedSet<ChannelContext> lockedSet = serverConfig.ips.clients(ip);
        if (lockedSet == null) {
            return;
        }

        lockedSet.handle(new ReadLockHandler<Set<ChannelContext>>() {
            @Override
            public void handle(Set<ChannelContext> set) {
                for (ChannelContext channelContext : set) {
                    Io.remove(channelContext, remark, closeCode);
                }
            }
        });
    }

    /**
     * 发送消息到指定ChannelContext
     */
    public static Boolean send(ChannelContext channelContext, Packet packet) {
        return send(channelContext, packet, null, null);
    }

    private static Boolean send(final ChannelContext channelContext, Packet packet, CountDownLatch countDownLatch, PacketSendMode packetSendMode) {
        try {
            if (packet == null || channelContext == null) {
                if (countDownLatch != null) {
                    countDownLatch.countDown();
                }
                return false;
            }


            if (channelContext.isClosed || channelContext.isRemoved) {
                if (countDownLatch != null) {
                    countDownLatch.countDown();
                }
                if (channelContext != null) {
                    log.info("can't send data, {}, isClosed:{}, isRemoved:{}", channelContext, channelContext.isClosed, channelContext.isRemoved);
                }
                return false;
            }

            if (channelContext.config.packetConverter != null) {
                Packet packet1 = channelContext.config.packetConverter.convert(packet, channelContext);
                if (packet1 == null) {
                    if (log.isInfoEnabled()) {
                        log.info("convert后为null，表示不需要发送", channelContext, packet);
                    }
                    return true;
                }
                packet = packet1;
            }

            boolean isSingleBlock = countDownLatch != null && packetSendMode == PacketSendMode.SINGLE_BLOCK;

            boolean isAdded = false;
            if (countDownLatch != null) {
                PacketMetaData packetMetaData = new PacketMetaData();
                packetMetaData.setCountDownLatch(countDownLatch);
                packet.setPacketMetaData(packetMetaData);
            }

            if (channelContext.config.useQueueSend) {
                isAdded = channelContext.sendQueueRunnable.addMsg(packet);
            } else {
                isAdded = channelContext.sendQueueRunnable.sendPacket(packet);
            }

            if (!isAdded) {
                if (countDownLatch != null) {
                    countDownLatch.countDown();
                }
                return false;
            }
            if (channelContext.config.useQueueSend) {
                channelContext.sendQueueRunnable.execute();
            }

            if (isSingleBlock) {
                long timeout = 10;
                try {
                    Boolean awaitFlag = countDownLatch.await(timeout, TimeUnit.SECONDS);
                    if (!awaitFlag) {
                        log.error("{}, 阻塞发送超时, timeout:{}s, packet:{}", channelContext, timeout, packet);
                    }
                } catch (InterruptedException e) {
                    log.error(e.toString(), e);
                }

                Boolean isSentSuccess = packet.getPacketMetaData().getSentSuccess();
                return isSentSuccess;
            } else {
                return true;
            }
        } catch (Throwable e) {
            log.error(channelContext + ", " + e, e);
            return false;
        }

    }

    /**
     * 发送到指定的ip和port
     */
    public static Boolean send(Config config, String ip, int port, Packet packet) {
        return send(config, ip, port, packet, false);
    }

    /**
     * 发送到指定的ip和port
     */
    private static Boolean send(Config config, String ip, int port, Packet packet, boolean isBlock) {
        ChannelContext channelContext = config.clientNodes.find(ip, port);
        if (channelContext != null) {
            if (isBlock) {
                return bSend(channelContext, packet);
            } else {
                return send(channelContext, packet);
            }
        } else {
            log.info("{}, can find channelContext by {}:{}", config.getName(), ip, port);
            return false;
        }
    }

    public static void sendToAll(Config config, Packet packet) {
        sendToAll(config, packet, null);
    }

    /**
     * 发消息到所有连接
     */
    public static void sendToAll(Config config, Packet packet, ChannelContextFilter channelContextFilter) {
        sendToAll(config, packet, channelContextFilter, false);
    }

    private static Boolean sendToAll(Config config, Packet packet, ChannelContextFilter channelContextFilter, boolean isBlock) {
        try {
            LockedSet<ChannelContext> lockedSet = config.connections;
            if (lockedSet == null) {
                log.debug("{}, 没有任何连接", config.getName());
                return false;
            }
            Boolean ret = sendToSet(config, lockedSet, packet, channelContextFilter, isBlock);
            return ret;
        } finally {
        }
    }

    /**
     * 发消息给指定业务ID
     */
    public static Boolean sendToBsId(Config config, String bsId, Packet packet) {
        return sendToBsId(config, bsId, packet, false);
    }

    /**
     * 发消息给指定业务ID
     */
    private static Boolean sendToBsId(Config config, String bsId, Packet packet, boolean isBlock) {
        ChannelContext channelContext = Io.getByBsId(config, bsId);
        if (channelContext == null) {
            return false;
        }
        if (isBlock) {
            return bSend(channelContext, packet);
        } else {
            return send(channelContext, packet);
        }
    }

    /**
     * 发消息到组
     */
    public static void sendToGroup(Config config, String group, Packet packet) {
        sendToGroup(config, group, packet, null);
    }

    /**
     * 发消息到组
     */
    public static void sendToGroup(Config config, String group, Packet packet, ChannelContextFilter channelContextFilter) {
        sendToGroup(config, group, packet, channelContextFilter, false);
    }

    /**
     * 发消息到组
     */
    private static Boolean sendToGroup(Config config, String group, Packet packet, ChannelContextFilter channelContextFilter, boolean isBlock) {
        try {
            LockedSet<ChannelContext> lockedSet = config.groups.clients(config, group);
            if (lockedSet == null) {
                log.debug("{}, 组[{}]不存在", config.getName(), group);
                return false;
            }
            Boolean ret = sendToSet(config, lockedSet, packet, channelContextFilter, isBlock);
            return ret;
        } finally {
        }
    }

    /**
     * 发消息给指定ChannelContext id
     */
    public static Boolean sendToId(Config config, String channelContextId, Packet packet) {
        return sendToId(config, channelContextId, packet, false);
    }

    /**
     * 发消息给指定ChannelContext id
     */
    private static Boolean sendToId(Config config, String channelContextId, Packet packet, boolean isBlock) {
        ChannelContext channelContext = Io.getChannelContextById(config, channelContextId);
        if (channelContext == null) {
            return false;
        }
        if (isBlock) {
            return bSend(channelContext, packet);
        } else {
            return send(channelContext, packet);
        }
    }

    /**
     * 发送到指定ip对应的集合
     */
    public static void sendToIp(Config config, String ip, Packet packet) {
        sendToIp(config, ip, packet, null);
    }

    /**
     * 发送到指定ip对应的集合
     */
    public static void sendToIp(Config config, String ip, Packet packet, ChannelContextFilter channelContextFilter) {
        sendToIp(config, ip, packet, channelContextFilter, false);
    }

    /**
     * 发送到指定ip对应的集合
     */
    private static Boolean sendToIp(Config config, String ip, Packet packet, ChannelContextFilter channelContextFilter, boolean isBlock) {
        try {
            LockedSet<ChannelContext> lockedSet = config.ips.clients(ip);
            if (lockedSet == null) {
                log.info("{}, 没有ip为[{}]的对端", config.getName(), ip);
                return false;
            }
            Boolean ret = sendToSet(config, lockedSet, packet, channelContextFilter, isBlock);
            return ret;
        } finally {
        }
    }

    /**
     * 发消息到指定集合
     */
    public static void sendToSet(Config config, LockedSet<ChannelContext> lockedSet, Packet packet, ChannelContextFilter channelContextFilter) {
        sendToSet(config, lockedSet, packet, channelContextFilter, false);
    }

    /**
     * 发消息到指定集合
     */
    private static Boolean sendToSet(Config config, LockedSet<ChannelContext> lockedSet, Packet packet, ChannelContextFilter channelContextFilter, boolean isBlock) {
        boolean releasedLock = false;
        Lock lock = lockedSet.readLock();
        lock.lock();
        try {
            Set<ChannelContext> set = lockedSet.getT();
            if (set.size() == 0) {
                log.debug("{}, 集合为空", config.getName());
                return false;
            }

            CountDownLatch countDownLatch = null;
            if (isBlock) {
                countDownLatch = new CountDownLatch(set.size());
            }
            int sendCount = 0;
            for (ChannelContext channelContext : set) {
                if (channelContextFilter != null) {
                    if (isBlock) {
                        countDownLatch.countDown();
                    }
                    continue;
                }

                sendCount++;
                if (isBlock) {
                    send(channelContext, packet, countDownLatch, PacketSendMode.GROUP_BLOCK);
                } else {
                    send(channelContext, packet, null, null);
                }
            }
            lock.unlock();
            releasedLock = true;

            if (sendCount == 0) {
                return false;
            }

            if (isBlock) {
                try {
                    long timeout = sendCount / 5;
                    timeout = Math.max(timeout, 10);//timeout < 10 ? 10 : timeout;
                    boolean awaitFlag = countDownLatch.await(timeout, TimeUnit.SECONDS);
                    if (!awaitFlag) {
                        log.error("{}, 同步群发超时, size:{}, timeout:{}, packet:{}", config.getName(), lockedSet.getT().size(), timeout, packet.toString());
                        return false;
                    } else {
                        return true;
                    }
                } catch (InterruptedException e) {
                    log.error(e.toString(), e);
                    return false;
                } finally {

                }
            } else {
                return true;
            }
        } catch (Throwable e) {
            log.error(e.toString(), e);
            return false;
        } finally {
            if (!releasedLock) {
                lock.unlock();
            }
        }
    }

    /**
     * 发消息到指定token
     */
    public static Boolean sendToToken(Config config, String token, Packet packet) {
        return sendToToken(config, token, packet, false);
    }

    /**
     * 发消息给指定token
     */
    private static Boolean sendToToken(Config config, String token, Packet packet, boolean isBlock) {
        LockedSet<ChannelContext> lockedSet = config.tokens.find(token);
        try {
            if (lockedSet == null) {
                return false;
            }

            ReadLock readLock = lockedSet.readLock();
            readLock.lock();
            try {
                Set<ChannelContext> set = lockedSet.getT();
                boolean ret = false;
                for (ChannelContext channelContext : set) {
                    boolean singleRet = false;
                    // 不要用 a = a || b()，容易漏执行后面的函数
                    if (isBlock) {
                        singleRet = bSend(channelContext, packet);
                    } else {
                        singleRet = send(channelContext, packet);
                    }
                    if (singleRet) {
                        ret = true;
                    }
                }
                return ret;
            } catch (Throwable e) {
                log.error(e.getMessage(), e);
            } finally {
                readLock.unlock();
            }
            return false;
        } finally {
        }
    }

    /**
     * 发消息给指定用户
     */
    public static Boolean sendToUser(Config config, String userid, Packet packet) {
        return sendToUser(config, userid, packet, false);
    }

    /**
     * 发消息给指定用户
     */
    private static Boolean sendToUser(Config config, String userid, Packet packet, boolean isBlock) {
        LockedSet<ChannelContext> lockedSet = config.users.find(userid);
        try {
            if (lockedSet == null) {
                return false;
            }

            ReadLock readLock = lockedSet.readLock();
            readLock.lock();
            try {
                Set<ChannelContext> set = lockedSet.getT();
                boolean ret = false;
                for (ChannelContext channelContext : set) {
                    boolean singleRet = false;
                    // 不要用 a = a || b()，容易漏执行后面的函数
                    if (isBlock) {
                        singleRet = bSend(channelContext, packet);
                    } else {
                        singleRet = send(channelContext, packet);
                    }
                    if (singleRet) {
                        ret = true;
                    }
                }
                return ret;
            } catch (Throwable e) {
                log.error(e.getMessage(), e);
            } finally {
                readLock.unlock();
            }
            return false;
        } finally {
        }
    }

    /**
     * 解绑业务id
     */
    public static void unbindBsId(ChannelContext channelContext) {
        channelContext.config.bsIds.unbind(channelContext);
    }

    /**
     * 与所有组解除解绑关系
     */
    public static void unbindGroup(ChannelContext channelContext) {
        channelContext.config.groups.unbind(channelContext);
    }

    /**
     * 与指定组解除绑定关系
     */
    public static void unbindGroup(String group, ChannelContext channelContext) {
        channelContext.config.groups.unbind(group, channelContext);
    }

    /**
     * 将某用户从组中解除绑定
     */
    public static void unbindGroup(Config config, String userid, String group) {
        LockedSet<ChannelContext> lockedSet = Io.getByUserid(config, userid);
        if (lockedSet != null) {
            lockedSet.handle(new ReadLockHandler<Set<ChannelContext>>() {
                @Override
                public void handle(Set<ChannelContext> set) {
                    for (ChannelContext channelContext : set) {
                        Io.unbindGroup(group, channelContext);
                    }
                }
            });
        }
    }

    /**
     * 解除channelContext绑定的token
     */
    public static void unbindToken(ChannelContext channelContext) {
        channelContext.config.tokens.unbind(channelContext);
    }

    /**
     * 解除token
     */
    public static void unbindToken(Config config, String token) {
        config.tokens.unbind(token);
    }


    /**
     * 解除channelContext绑定的userid
     */
    public static void unbindUser(ChannelContext channelContext) {
        channelContext.config.users.unbind(channelContext);
    }

    /**
     * 解除userid的绑定。一般用于多地登录，踢掉前面登录的场景
     */
    public static void unbindUser(Config config, String userid) {
        config.users.unbind(userid);
    }

    private Io() {
    }

}
